package impl;

import java.io.FileWriter;
import java.io.IOException;
import java.rmi.AlreadyBoundException;
import java.rmi.NoSuchObjectException;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import tools.MMMEDirectory;

import agent.FragmentAgent;
import components.QuasiComponentBehavior;
import components.QuasiComponentCharacteristics;

import fragmentAppl.Representations;
import fragmentAppl.Skills;

public class MediumImpl implements Medium {

  private static volatile int n = 0;

  private final int port;

  private final ExecutorService executor;
  
  public MediumImpl(int port) {
    this.port = port;
    this.executor = Executors.newCachedThreadPool();

    RemoteMedium rmedium = new RemoteMedium() {
      public AgentRefImpl create(QuasiComponentCharacteristics c,QuasiComponentBehavior beh) throws RemoteException {
        return MediumImpl.this.realCreate(c,beh);
      }

	@Override
	public AgentRefImpl create(QuasiComponentBehavior beh)
			throws RemoteException {
		// TODO Auto-generated method stub
		return null;
	}

    };

  }

//    try {
//      System.setProperty("java.security.policy", "java.policy");
//      System.setSecurityManager(new RMISecurityManager());
//
//      // reuse port instead of opening another one!
//      UnicastRemoteObject.exportObject(rmedium, port);
//      (LocateRegistry.createRegistry(port)).bind("ActorMedium", rmedium);
//    } catch (RemoteException e) {
//      throw new RuntimeException("createRegistry failed", e);
//    } catch (AlreadyBoundException e) {
//      throw new RuntimeException("createRegistry failed", e);
//    }
//  }

  // TODO there is a difference between the ref for message and the ref for the medium!!!!!!!!!!! 
  
  
  // TODO cast is not good -> because we know that we use this impl
  // this is one of the tricky point for environment
  public void send(AgentRef r, Message message) {
    this.realSend((AgentRefImpl) r, message);
  }
  
  public void disconnect(AgentRef r) {
    this.realDisconnect((AgentRefImpl) r);
  }
  


  public void realSend(AgentRefImpl r, Message message) {
    try {
      r.agent.receive(message);
    } catch (NoSuchObjectException e) {
      // TODO do nothing, the agent is not here anymore, that's all
    } catch (RemoteException e) {
      throw new RuntimeException("send failed", e);
    }
  }

  public void realDisconnect(AgentRefImpl r) {
    RemoteAgent agent = r.agent;
    try {
      UnicastRemoteObject.unexportObject(agent, true);
    } catch (NoSuchObjectException e) {
      throw new RuntimeException("unexport failed", e);
    }
  }
  
 
  // TODO the trick is that Message is created before Myself so the ref can be initialized
  private AgentRefImpl realCreate(QuasiComponentCharacteristics caract ,QuasiComponentBehavior beh) {
    MessageImpl m = new MessageImpl(this);

    AgentRefImpl ref = this.init(m);
    CreateImpl c = new CreateImpl(this,ref);
 
    final FragmentAgent a = new FragmentAgent(c,m, new LifeCycleImpl(),caract,new Representations(),new Skills(),beh);
    
    		
    a.start();   
   System.out.println(a.getId().toString()+ " start");
    
    return ref;
  }
  
  private AgentRefImpl init(final MessageImpl m) {
    int i = n++;
    AgentRefImpl a;

    // TODO beware that anonymous instance of classes are kept linked to this and so are not gc'ed when we would want
    a = new AgentRefImpl(new RemoteAgentImpl(m), "agent" + i + "@" );

    try {
      // reuse port instead of opening another one!
      UnicastRemoteObject.exportObject(a.agent, port);
    } catch (RemoteException e) {
      throw new RuntimeException("export failed", e);
    }
    return a;
  }
  
  private static class RemoteAgentImpl implements RemoteAgent {

    private final MessageImpl m;
    
    public RemoteAgentImpl(final MessageImpl m) {
      this.m = m;
    }
    
    public void receive(Message message) throws RemoteException {
      m.receive(message);
    }
  }



public void realSend(EntityRefImpl r, Message message) {
    try {
      r.entity.receive(message);
    } catch (NoSuchObjectException e) {
      // TODO do nothing, the agent is not here anymore, that's all
    } catch (RemoteException e) {
      throw new RuntimeException("send failed", e);
    }
  }

@Override
public AgentRef create(QuasiComponentCharacteristics c,
		QuasiComponentBehavior beh) {
	// TODO Auto-generated method stub
	 return realCreate(c,beh);
}



}
